#region license
/*
 * Copyright (C) 2007 CchenSoft.com
 * 
 * This library is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License 2.1 or later, as
 * published by the Free Software Foundation. See the included License.txt
 * or http://www.gnu.org/copyleft/lesser.html for details.
 * 
 * Author: Billy Zhang
 */
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Collections;
using System.Reflection;
using CchenSoft.Portal.Spi.Plugin;
using CchenSoft.Portal.Spi.Service;
using CchenSoft.Portal.Spi.Registry;

namespace CchenSoft.Portal.Spi.Plugin
{
    public class ServicePlugin : GeneralPlugin, IServicePlugin
    {
        protected string category;
        protected IDictionary<string, IPluginService> services;
        protected IDictionary<string, ServiceEntry> entries;

        public T GetService<T>()
        {
            string key = typeof(T).Name;

            if (entries.ContainsKey(key))
            {
                ServiceEntry entry = entries[key];
                if (entry.Instance)
                {
                    IPluginService service = (IPluginService)Activator.CreateInstance(entry.Type);

                    foreach (string name in entry.Properties.Keys)
                    {
                        PropertyInfo pi = service.GetType().GetProperty(name);
                        if (pi != null)
                        {
                            object val = entry.Properties[name]; // Convert.ChangeType(entry.Properties[name], pi.PropertyType);
                            pi.SetValue(service, val, null);
                        }
                    }

                    service.SetPlugin(this);
                    service.Initialize();

                    return (T)service;
                }
                else
                {
                    if (services.ContainsKey(key))
                        return (T)services[key];
                }
            }

            return default(T);
        }

        public string Category
        {
            get { return category; }
        }

        public override void Configure(XmlDocument doc)
        {
            base.Configure(doc);
            
            entries = new Dictionary<string, ServiceEntry>();

            category = GetNodeValue(doc.DocumentElement.SelectSingleNode("category"));

            // parse services
            services = ParseServices(doc.DocumentElement.SelectNodes("service"));
        }

        #region helper methods.

        private IDictionary<string, IPluginService> ParseServices(XmlNodeList list)
        {
            IDictionary<string, IPluginService> services = new Dictionary<string, IPluginService>();

            foreach (XmlNode node in list)
            {
                ServiceEntry entry = new ServiceEntry();

                entry.Name = GetNodeValue(node.Attributes.GetNamedItem("name"));
                entry.Type = Type.GetType(GetNodeValue(node.Attributes.GetNamedItem("type")));
                entry.Instance = GetNodeValue(node.Attributes.GetNamedItem("instance")) == "true";

                XmlNodeList list2 = node.SelectNodes("property");
                IDictionary<string, object> properties = new Dictionary<string, object>();

                foreach (XmlNode node2 in list2)
                {
                    string name = node2.Attributes.GetNamedItem("name").Value;
                    object value = null;

                    if (node2.ChildNodes.Count > 0)
                    {
                        XmlNode valueNode = node2.ChildNodes[0];

                        if (valueNode.Name.Equals("type"))
                        {
                            Type type = Type.GetType(valueNode.InnerText);
                            if (type != null)
                                value = Activator.CreateInstance(type);
                        }
                        else if (valueNode.Name.Equals("boolean"))
                        {
                            value = valueNode.InnerText.Equals("true");
                        }
                        else if (valueNode.Name.Equals("integer"))
                        {
                            value = Convert.ToInt32(valueNode.InnerText);
                        }
                        else if (valueNode.Name.Equals("list"))
                        {
                            ArrayList items = new ArrayList();

                            XmlNodeList itemList = valueNode.SelectNodes("item");
                            foreach (XmlNode itemNode in itemList)
                            {
                                items.Add(itemNode.InnerText);
                            }

                            value = list;
                        }
                        else
                        {
                            value = node2.InnerText;
                        }
                    }

                    properties.Add(name, value);
                }

                entry.Properties = properties;
                entries.Add(entry.Name, entry);

                if (!entry.Instance)
                {
                    if (entry.Type != null)
                    {
                        IPluginService service = (IPluginService)Activator.CreateInstance(entry.Type);

                        foreach (string name in entry.Properties.Keys)
                        {
                            PropertyInfo pi = service.GetType().GetProperty(name);
                            if (pi != null)
                            {
                                object val = entry.Properties[name]; // Convert.ChangeType(entry.Properties[name], pi.PropertyType);
                                pi.SetValue(service, val, null);
                            }
                        }

                        service.SetPlugin(this);
                        service.Initialize();
                        services.Add(entry.Name, service);
                    }
                }
            }

            return services;
        }

        #endregion
    }
}
